# -*- coding: utf-8 -*-
#----------------------------------------------------------------------------#
# kanjidic.py
# Lars Yencken <lljy@csse.unimelb.edu.au>
# ZhihuiJin    <zhihuijin@gmail.com>
# vim: ts=4 sw=4 sts=4 et tw=78:
# Fri Apr 28 16:09:09 2006
#
#----------------------------------------------------------------------------#

""" A nice interface to the Kanjidic dictionary.
"""

#----------------------------------------------------------------------------#

from tools.smartCache import diskProxyDirect
from tools.common import smartLineIter
from tools import kana

from www import settings

import re
from os import path

#----------------------------------------------------------------------------#

defaultKanjidic = [
        path.join(settings.DATA_DIR, 'kanjidic'),
        path.join(settings.DATA_DIR, 'kanjd212')
    ]

defaultKradfile = [ path.join(settings.DATA_DIR, 'kradfile') ]

#----------------------------------------------------------------------------#

basicFeatures = set([
        'gloss',
        'strokeCount',
        'jyouyouGrade',
        'skipCode',
        'onyomi',
        'kunyomi',
        'unicode'
    ])

#----------------------------------------------------------------------------#

remappings = {
        'B':    'radicalIndex',
        'C':    'classicalRadicalIndex',
        'F':    'frequency',
        'G':    'jyouyouGrade',
        'H':    'halpernIndex',
        'N':    'nelsonIndex',
        'V':    'newNelsonIndex',
        'D':    'dictionaryCode',
        'P':    'skipCode',
        'S':    'strokeCount',
        'U':    'unicode',
        'I':    'spahnCode',
        'Q':    'fourCornerCode',
        'M':    'morohashiIndex',
        'E':    'henshallCode',
        'K':    'gakkenCode',
        'L':    'heisigCode',
        'O':    'oneillCode',
        'W':    'koreanReading',
        'Y':    'pinyinReading',
        'X':    'crossReferences',
        'Z':    'misclassifiedAs'
    }

#----------------------------------------------------------------------------#

class KanjidicEntry:
    """ A single entry in the kanjidic file.
    """
    def __init__(self, entryDetails):
        self.__dict__.update(entryDetails)
        return

#----------------------------------------------------------------------------#

class Kanjidic(dict):
    """ An interface to the kanjidic dictionary. Create a new instance to parse
        the dictionary. Has a nice getitem interface to get the details of a
        character.
    """

    #------------------------------------------------------------------------#
    # PUBLIC METHODS
    #

    def __init__(self, kanjidicFiles=None):
        """ Constructor.

            @param kanjidicFiles: The files corresponding to kanjidic.
        """
        dict.__init__(self)

        if kanjidicFiles is None:
            kanjidicFiles = defaultKanjidic

        self._parseKanjidic(kanjidicFiles)
        return

    #------------------------------------------------------------------------#
    # PRIVATE METHODS
    #

    def _parseKradfile(self, files):
        """ Parse the kradfile, get the radicals for each kanji

            @param files: The files for kanji radicals.
        """
        segmentPattern = re.compile('[^ :]+', re.UNICODE)
        radicalDic = {}

        for line in smartLineIter(files):
            if line.startswith('#'):
                continue

            segments = segmentPattern.findall(line.strip())
            kanji    = segments[0]
            radicalDic[kanji] = segments[1:]

        return radicalDic

    #------------------------------------------------------------------------#

    def _getReading(self, segment):
        segs = segment.replace('-', '').split('.')
        segment = segs[0]

        charToCheck = segment[0]
        if kana.scriptType(charToCheck) == kana.Script.Katakana:
            segment = kana.toHiragana(segment)
        return segment

    #------------------------------------------------------------------------#


    def _parseKanjidic(self, files):
        """ Parses the kanjidic file for its contents, updating this class
            with a kanji -> info mapping.

            @param files: The files for kanjidic.
        """
        segmentPattern = re.compile('[^ {]+|{.*?}', re.UNICODE)

        self._dictionary = {}
        radicalDic = self._parseKradfile(defaultKradfile)

        for line in smartLineIter(files):
            if line.startswith('#'):
                continue

            segments = segmentPattern.findall(line.strip())
            onReadings  = set()
            kunReadings = set()

            kanji = segments[0]
            jisCode = int(segments[1], 16)
            kanjiInfo = {
                    'kanji':        kanji,
                    'gloss':        [],
                    'onReadings':   [],
                    'kunReadings':  [],
                    'jisCode':      jisCode
                }

            i = 2
            numSegments = len(segments)
            while i < numSegments:
                thisSegment = segments[i]
                i += 1

                # handle glosses
                if thisSegment.startswith('{'):
                    kanjiInfo['gloss'].append(thisSegment[1:-1])

                # handle readings
                elif kana.scriptType(thisSegment) != kana.Script.Ascii or \
                        thisSegment.startswith('-'):
                    charToCheck = thisSegment[0]
                    if charToCheck == '-':
                        charToCheck = thisSegment[1]

                    if kana.scriptType(charToCheck) == kana.Script.Katakana:
                        #kanjiInfo['onReadings'].append(reading)
                        onReadings.add(self._getReading(thisSegment))
                    elif kana.scriptType(charToCheck) == kana.Script.Hiragana:
                        #kanjiInfo['kunReadings'].append(thisSegment)
                        kunReadings.add(self._getReading(thisSegment))
                    else:
                        raise Exception, "Unknown segment %s" % thisSegment

                elif thisSegment in ('T1', 'T2'):
                    continue

                else:
                    # handle various codes
                    code = thisSegment[0]
                    remainder = thisSegment[1:]
                    try:
                        remainder = int(remainder)
                    except:
                        pass

                    kanjiInfo.setdefault(remappings[code], []).append(
                        remainder)
                 
            # end while

            kanjiInfo['onReadings']  = list(onReadings)
            kanjiInfo['kunReadings'] = list(kunReadings)
            kanjiInfo['strokeCount'] = kanjiInfo['strokeCount'][0]
            if kanjiInfo.has_key('frequency'):
                kanjiInfo['frequency'] = int(kanjiInfo['frequency'][0])

            kanjiInfo['skipCode'] = tuple(map(
                    int,
                    kanjiInfo['skipCode'][0].split('-')
                ))

            kanjiInfo['radicals'] = radicalDic.get(kanji, list())


            kanjidicEntry = KanjidicEntry(kanjiInfo)
            self.__setitem__(kanji, kanjidicEntry)

        return

    #------------------------------------------------------------------------#

#----------------------------------------------------------------------------#

def getKanjidic():
    """ Fetches and caches a copy of Kanjidic.
    """
    global _cachedKanjidic

    if _cachedKanjidic is None:
        fetchKanjidic = diskProxyDirect(
                Kanjidic,
                path.join(settings.CACHE_DIR, 'kanjidic.cache'),
                dependencies=defaultKanjidic + [__file__],
            )
        _cachedKanjidic = fetchKanjidic()

    return _cachedKanjidic

#----------------------------------------------------------------------------#

def delKanjidic():
    """ Remove references to kanjidic.
    """
    global _cachedKanjidic
    _cachedKanjidic = None
    return

#----------------------------------------------------------------------------#
# PRIVATE
#----------------------------------------------------------------------------#

# An in-memory cached version.
_cachedKanjidic = None

#----------------------------------------------------------------------------#

# vim: ts=4 sw=4 sts=4 et tw=78:
